﻿using System;
using System.Collections;
using System.Collections.Generic;
using SimpleCore.Tool;
using UnityEngine;

namespace SimpleCore
{
    public partial class TimeTool : MonoBehaviour
    {
        //下面的协程方法中不进行 NULL 的判断。原因如下：
        //1. 传入NULL是错误的做法，需要报错提示
        //2. 唯一对外接口有对 NULL 的报错提示处理
    
        /// <summary>
        /// 延迟执行
        /// </summary>
        /// <param name="delay">延迟时间</param>
        /// <param name="action">执行函数</param>
        /// <returns></returns>
        static IEnumerator IEWaitToDo(float delay, Action action)
        {
            yield return YieldTool.WaitForSeconds(delay);
        
            action();
        }

        /// <summary>
        /// 延迟执行（条件达成执行）
        /// </summary>
        /// <param name="condition">判断条件(为true开始执行)</param>
        /// <param name="action">执行函数</param>
        /// <returns></returns>
        static IEnumerator IEWaitToDo(Func<bool> condition, Action action)
        {
            yield return new WaitUntil(condition);

            action();
        }

        /// <summary>
        /// 每帧执行
        /// </summary>
        /// <param name="condition">判断条件（为true执行）</param>
        /// <param name="action">执行函数</param>
        /// <returns></returns>
        static IEnumerator IEUpdateToDo(Func<bool> condition, Action action)
        {
            while (condition())
            {
                action();
                yield return null;
            }
        }

        /// <summary>
        /// 定时循环执行
        /// </summary>
        /// <param name="interval">时间间隔</param>
        /// <param name="condition">判断条件（为true执行）</param>
        /// <param name="onUpdate">执行函数</param>
        /// <param name="onCompleted"></param>
        /// <returns></returns>
        static IEnumerator IERepeatToDo(float interval, Func<bool> condition, Action onUpdate, Action onCompleted)
        {
            while (condition())
            {
                onUpdate();
                yield return new WaitForSeconds(interval);
            }
            
            onCompleted?.Invoke();
        }
    }

    public partial class TimeTool
    {
        /// <summary>
        /// 延迟执行
        /// </summary>
        /// <param name="delay">延迟时间</param>
        /// <param name="action">执行函数</param>
        public static void WaitToDo(float delay, Action action)
        {
            if (delay < Accuracy || action == null)
            {
                Debug.LogError("参数错误，不予执行");
                return;
            }

            GetInstance().StartCoroutine(IEWaitToDo(delay, action));
        }

        /// <summary>
        /// 条件达成执行
        /// </summary>
        /// <param name="condition">判断条件（为true执行）</param>
        /// <param name="action">执行函数</param>
        public static void WaitToDo(Func<bool> condition, Action action)
        {
            if (condition == null || action == null)
            {
                Debug.LogError("参数错误，不予执行");
                return;
            }

            GetInstance().StartCoroutine(IEWaitToDo(condition, action));
        }

        /// <summary>
        /// 每帧执行
        /// </summary>
        /// <param name="condition">判断条件（每帧执行）</param>
        /// <param name="action">执行函数</param>
        public static void UpdateToDo(Func<bool> condition, Action action)
        {
            if (condition == null || action == null)
            {
                Debug.LogError("参数错误，不予执行");
                return;
            }

            GetInstance().StartCoroutine(IEUpdateToDo(condition, action));
        }

        /// <summary>
        /// 定时循环执行
        /// </summary>
        /// <param name="interval">时间间隔</param>
        /// <param name="condition">判断条件（为true执行）</param>
        /// <param name="onUpdate">执行函数</param>
        public static void RepeatToDo(float interval, Func<bool> condition, Action onUpdate, Action onCompleted)
        {
            if (interval < Accuracy || condition == null || onUpdate == null)
            {
                Debug.LogError("参数错误，不予执行");
                return;
            }

            GetInstance().StartCoroutine(IERepeatToDo(interval, condition, onUpdate, onCompleted));
        }

        /// <summary>
        /// 延迟每帧执行
        /// </summary>
        /// <param name="delay"></param>
        /// <param name="condition"></param>
        /// <param name="action"></param>
        public static void WaitToUpdate(float delay, Func<bool> condition, Action action)
        {
            if (delay < Accuracy || condition == null || action == null)
            {
                Debug.LogError("参数错误，不予执行");
                return;
            }
            WaitToDo(delay, () => UpdateToDo(condition, action));
        }

        /// <summary>
        /// 条件达成后每帧执行
        /// </summary>
        /// <param name="delayCondition"></param>
        /// <param name="updateCondition"></param>
        /// <param name="action"></param>
        public static void WaitToUpdate(Func<bool> delayCondition, Func<bool> updateCondition, Action action)
        {
            if (delayCondition == null || updateCondition == null || action == null)
            {
                Debug.LogError("参数错误，不予执行");
                return;
            }
            WaitToDo(delayCondition, () => UpdateToDo(updateCondition, action));
        }

        /// <summary>
        /// 延迟定时循环
        /// </summary>
        /// <param name="delay"></param>
        /// <param name="interval"></param>
        /// <param name="condition"></param>
        /// <param name="onUpdate"></param>
        /// <param name="onCompleted"></param>
        public static void WaitToRepeat(float delay, float interval, Func<bool> condition, Action onUpdate, Action onCompleted)
        {
            if (delay < Accuracy || interval < Accuracy || condition == null || onUpdate == null)
            {
                Debug.LogError("参数错误，不予执行");
                return;
            }

            WaitToDo(delay, () => RepeatToDo(interval, condition, onUpdate, onCompleted));
        }

        /// <summary>
        /// 条件达成后定时循环
        /// </summary>
        /// <param name="delayCondition"></param>
        /// <param name="interval"></param>
        /// <param name="updateCondition"></param>
        /// <param name="onUpdate"></param>
        /// <param name="onCompleted"></param>
        public static void WaitToRepeat(Func<bool> delayCondition, float interval, Func<bool> updateCondition, Action onUpdate, Action onCompleted)
        {
            if (delayCondition == null || interval < Accuracy || updateCondition == null || onUpdate == null)
            {
                Debug.LogError("参数错误，不予执行");
                return;
            }
            WaitToDo(delayCondition, () => RepeatToDo(interval, updateCondition, onUpdate, onCompleted));
        }

        static TimeTool GetInstance()
        {
            if (_instance == null)
            {
                GameObject go = new GameObject("SFw -- Time Tool");
                DontDestroyOnLoad(go);
                _instance = go.AddComponent<TimeTool>();
            }

            return _instance;
        }

        private const float Accuracy = 0.00001f;//float比较精度值
        private static TimeTool _instance;
    }
}